﻿using System;
using System.Collections.Generic;
using System.Linq;
using NBehave.Narrator.Framework;
using NBehave.Spec.NUnit;
using NH.Elateral.PipeManagement.Core;
using NUnit.Framework;
using Rhino.Mocks;
using Context = NUnit.Framework.TestFixtureAttribute;
using That = NUnit.Framework.TestAttribute;
using NH.Elateral.PipeManagement.Core.Interfaces;


namespace NH.Elateral.PipeManagement.Scenarios
{


	[Theme("Pipe Management System Test"), Context]
	public class PipeManagerSpec : SpecBase
	{
		private Story _story;
		private IPipeRepository _pipeRepository = null;

		public override  void MainSetup()
		{
			_story = new Story("On PipeManager intialization I expect the system to show the current status of all the pipes");
			_story
				.AsA("User")
				.IWant("to know the current status of all the pipes")
				.SoThat("I know the system has registered and monitoring all the pipes.");

			base.SpecSetup();
		}

		[SetUp]
		public void Initialize_before_each_test()
		{
			_pipeRepository = MockRepository.GenerateMock<IPipeRepository>();
		
			
		}

		[Story, That]
		public void Expect_to_know_statuses_of_all_pipes()
		{
			IPipeStatusFormatter statusFormater = null;
			string status = string.Empty;
			string expectedStatus = "a,a,a,a,a,a,a,a,a,a";
			IList<Pipe> collectionOfPipes = Generate_collection_of_pipes_with_default_state(10, true);


			_story
			.WithScenario("Given a collection of 10 pipes, I want to know the state off all 10 pipes")
			.Given("A Mock PipeRepository which returns a collection of 10 pipes", () =>
			{
				_pipeRepository.Expect(action => action.GetAllPipes()).Return(collectionOfPipes);
			})
			.And("a PipeStatusFormater which returns a status of 'a' regardless of actual pipe status",
					() =>
					{
						statusFormater = MockRepository.GenerateStub<IPipeStatusFormatter>();
						statusFormater.Stub(action => action.Format(null)).IgnoreArguments().Return("a");
					})

			.When("requesting status", () =>
			{
				var pipeManager = new PipeManager(_pipeRepository, statusFormater);
				status = pipeManager.GetPipeStatus();
			})
			.Then("I expect the repository to return the collection of pipes", () => _pipeRepository.VerifyAllExpectations())
			.And("I expect a string containing char:'a' repeated 10x", () => status.ShouldEqual(expectedStatus));

		}
		
		
		[Story, That]
		public void Expect_proper_formatted_status_message()
		{
			IPipeStatusFormatter statusFormatter =null;
			string status = String.Empty;
			string expectedStatus = "Pipe,1,open,Pipe,2,open";

			_story
				.WithScenario("Given an actual implementation of PipeStatusFormatter, I expect a correctly formatted status")
			.Given("A Mock Repository which retuns a collection of 2 pipes", () => 
				{
					_pipeRepository
						.Expect(action => action.GetAllPipes())
						.Return( Generate_collection_of_pipes_with_default_state(2, true));
				})
			.And("a concrete implementation of IPipeStatusFormatter", () => statusFormatter = new PipeStatusFormatterCsv())

			.When("requesting for system status", () =>
			{
				var pipeManager = new PipeManager(_pipeRepository, statusFormatter);
				status = pipeManager.GetPipeStatus();
			})
			.Then("I expect that the repository to return the collection", () => _pipeRepository.VerifyAllExpectations())
			.And(String.Format("status message should be :{0}", expectedStatus),() => StringAssert.IsMatch(expectedStatus, status));
			
		}

		
		[Story, Test]
		public void Should_be_able_to_open_pipe1()
		{
			Should_be_able_to_interact_with_pipe1(false, true, "Pipes.Open1");
				
		}

		[Story, Test]
		public void Should_be_able_to_close_pipe1()
		{
			Should_be_able_to_interact_with_pipe1(true, false, "Pipes.Close1");
		}

		[Story, Test]
		public void Should_be_able_to_interact_with_multiple_pipes()
		{
			IPipeStatusFormatter statusFormatter = null;
			string sampleCommand = "Pipes.Open1,Open2,Open3,Close4,Open6,Close9";
			string result = null;
			string expectedResult = "Pipe,1,open,Pipe,2,open,Pipe,3,open,Pipe,4,closed,Pipe,5,closed,Pipe,6,open,Pipe,7,closed,Pipe,8,closed,Pipe,9,closed,Pipe,10,closed";

			_story
				.WithScenario("Given a correctly formatted command string," +
				" the PipeManager can change corresponding state of any given pipes")
				.Given("A mock repository which returns 10 pipes with initial state of 'closed'",
					() => _pipeRepository
							.Expect(action => action.GetAllPipes())
							.Return(Generate_collection_of_pipes_with_default_state(10, false))
							)
				.And("A concrete implementation of PipeStatusFormatter",
					() => statusFormatter = new PipeStatusFormatterCsv()
					)
				.When("PipeManager is initialize and a string command is passed to it",
					() =>
					{
						var pipeManager = new PipeManager(_pipeRepository, statusFormatter, new CommandParser());
						result = pipeManager.ActOn(sampleCommand);
					})
				.Then("PipeRepository returned 10 pipes", () => _pipeRepository.VerifyAllExpectations())
				.And("The resultant status should match expectedResult",() => StringAssert.IsMatch(expectedResult, result));
		}


		private void Should_be_able_to_interact_with_pipe1(bool initialDefaultStateOfAllPipes, bool desiredStateOfPipe1, string command)
		{
			IPipeStatusFormatter statusFormater = null;
			IList<Pipe> collectionOfPipes = Generate_collection_of_pipes_with_default_state(2, false);


			_story
				.WithScenario("PipeManager should be able to {0} pipe1 when given a command and returns statuses of all pipes.",
					desiredStateOfPipe1 ? "(Open)" : "(Close)")
				.Given("A mock PipeRepository which returns a collection of 2 pipes with inital states of close",
					() => _pipeRepository.Expect(action =>
							action
							.GetAllPipes())
							.Return(collectionOfPipes)
					)
				.And("a concrete implementation of PipeStatusFormatter",
					() => statusFormater = new PipeStatusFormatterCsv())
				.When("PipeManager is issued with command.", command, incomingCmd =>
				{
					var pipeManager = new PipeManager(_pipeRepository, statusFormater, new CommandParser());
					pipeManager.ActOn(command);
				})
				.Then("PipeRepository returns the right a collection of 2 pipes", () => _pipeRepository.VerifyAllExpectations())
				.And("I expect the the pipe with ID:1 is open",
					() =>
					{
						IQueryable<Pipe> pipes = new List<Pipe>(collectionOfPipes).AsQueryable();
						Pipe p = pipes.WherePipeHaveId(1);
						p.ShouldNotBeNull();

						if (desiredStateOfPipe1)
							p.IsOpen.ShouldBeTrue();
						else
							p.IsOpen.ShouldBeFalse();
					});
		}

		private IList<Pipe> Generate_collection_of_pipes_with_default_state(int numberOfPipes, bool defaultState)
		{
			numberOfPipes.ShouldBeGreaterThan(0);

			List<Pipe> temp = new List<Pipe>();
			for (int i = 0; i < numberOfPipes; i++)
			{
				temp.Add(new Pipe(i + 1, defaultState));
			}
			return temp;
		}
	}
}
